/*   * TODO put header   */package com.byq.mm.smartlib.obd.commands;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

/** * TODO put description */
public abstract class AbstractObdCommand implements ObdCommand {
	protected ArrayList<Integer> buffer = null;
	protected String cmd = null;
	protected boolean useImperialUnits = false;
	protected String rawData = null;
	static Set<String> stopwords = new HashSet<String>(Arrays.asList(new String[]{"DATA", "OK", "SEARCHING"}));
	/** * Default ctor to use * * @param command * the command to send */
	public AbstractObdCommand(String command) {
		this.cmd = command;
		this.buffer = new ArrayList<Integer>();
	}

	/** * Prevent empty instantiation */
	private AbstractObdCommand() {
	}
	protected void simpleRead(InputStream in) throws IOException {

	
			byte b = 0;
			StringBuilder res = new StringBuilder();
			while ((char) (b = (byte) in.read()) != '>')
				if ((char) b != ' ')
					res.append((char) b); /*                   
										 * * Imagine the following response 41 0c 00
										 * 0d. * * ELM sends strings!! So, ELM puts
										 * spaces between each "byte". And pay *
										 * attention to the fact that I've put the
										 * word byte in quotes, because * 41 is
										 * actually TWO bytes (two chars) in the
										 * socket. So, we must do * some more
										 * processing..
										 */
			rawData = res.toString().trim();

		
	}
	/** * Copy ctor. * * @param other * the ObdCommand to copy. */
	public AbstractObdCommand(AbstractObdCommand other) {
		this(other.cmd);
	}

	/* (non-Javadoc)
	 * @see com.byq.mm.obd.commands.ObdCommand#run(java.io.InputStream, java.io.OutputStream)
	 */
	@Override
	public void run(InputStream in, OutputStream out) throws IOException,
			InterruptedException {
		sendCommand(out);
		simpleRead(in);
		if(isNumber())
			parse();
	}

	private boolean isNumber() {
		if(rawData == null)
			return false;
		
		String rst = this.rawData.trim().split(">")[0];
		for(String token : stopwords)
			if (rst.contains(token)) {
				return false;
			}
		return true;
	}

	/**
	 * * Sends the OBD-II request. * * This method may be overriden in
	 * subclasses, such as ObMultiCommand or * TroubleCodesObdCommand. * * @param
	 * cmd * The command to send.
	 */
	protected void sendCommand(OutputStream out) throws IOException,
			InterruptedException {
		cmd += "\r";
		out.write(cmd.getBytes());
		out.flush(); /*                   
					 * * HACK GOLDEN HAMMER ahead!! * * TODO clean * * Due to
					 * the time that some systems may take to respond, let's
					 * give it * 500ms.
					 */
		Thread.sleep(200);
	}

	/** * Resends this command. * * */
	protected void resendCommand(OutputStream out) throws IOException,
			InterruptedException {
		out.write("\r".getBytes());
		out.flush(); /*                   
					 * * HACK GOLDEN HAMMER ahead!! * * TODO clean this * * Due
					 * to the time that some systems may take to respond, let's
					 * give it * 500ms.
					 */
	}


	private void parse() {
		buffer.clear();
		int begin = 0;
		int end = 2;
		String data =  this.rawData.trim().split(">")[0].trim();
		String ss = data.split("\r")[0];
		while (end <= ss.length()) {
			String temp = "0x" + ss.substring(begin, end);
			try {
				buffer.add(Integer.decode(temp));
			} catch (NumberFormatException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				buffer.clear();
				return;
			}
			begin = end;
			end += 2;
		}
	}


	public String getResult() {
		
		if (!isNumber()) {
			rawData = "NODATA";
		}
		return rawData;
	}


	public abstract String getFormattedResult();

	public ArrayList<Integer> getBuffer() {
		return buffer;
	}
	public byte[] getByteResult(){
		byte[] rst = new byte[buffer.size()];
		for(int i = 0; i < rst.length; i++)
			rst[i] = buffer.get(i).byteValue();
		return rst;
	}
	/** * Returns this command in string representation. * * @return the command */
	public String getCommand() {
		return cmd;
	}

	/** * @return true if imperial units are used, or false otherwise */
	public boolean useImperialUnits() {
		return useImperialUnits;
	}

	/**
	 * * Set to 'true' if you want to use imperial units, false otherwise. By *
	 * default this value is set to 'false'. * * @param isImperial
	 */
	public void useImperialUnits(boolean isImperial) {
		this.useImperialUnits = isImperial;
	}
	 public String getResponseKey(){
		 byte[] response = getByteResult();
		 if(response.length == 0)
			 return "0";
		 return "" + response[0] + response[1];
	 }

	public abstract String getName();
}